package xml.parserDTD;

import java.util.ArrayList;

import xml.parserDOM.Arbol;
import xml.parserDOM.Nodo;
import xml.parserSAX.SAXException;
import Clases.Atributo;
import Clases.Tag;

public class DTDSaxHandler {
    private boolean firts = true;
    private boolean PrimerElement = true;

	/**
	 * 
	 * esta funcion se encarga de validar el arbol xml a partir del dtd.
	 * 
	 * @param arbol
	 * @param tipo
	 * @param qName
	 * @param elementos
	 * @param qAtributo
	 * @param atributoTipo
	 * @param isRequired
	 * @throws SaxException
	 */
	public void startElement(Arbol arbol, String tipo, String qName,
			ArrayList<ElementoDtd> elementos, String qAtributo,
			String atributoTipo, Boolean isRequired) throws SAXException {

        // Busco en el arbol todos los nodos que coinsidan con
        // el objeto elementoDtd
        ArrayList<Nodo<Tag>> lista = new ArrayList<Nodo<Tag>>();

        lista = getListadoDeNodosPorTag(qName, arbol.getRootElement());

        // Pregunto si lo que leyo es un ELEMENT
        if (tipo.equals("ELEMENT")) {

            // Pregunto si es la primera ves que leo un !ELEMENT
            if (PrimerElement) {
                PrimerElement = false;

                // Pregunto si es el elemento raiz. Sino disparo un error.
                if (!arbol.getDocType().equals(qName)) {
					throw new SAXException(SAXException.Tipo.ERROR,
                                    "El nombre del DTD y el elemento raiz no coiniden");
                }
            }
            // Valido que todos los hijos del Padre cumplan con la propiedad
            // descrita en element
            for (ElementoDtd elementoDtd : elementos) {

                switch (elementoDtd.getCardinalidad()) {
                case '+':

                    // Recorro cada uno de los objetos de la lista
                    // (supustamente son todos los objetos del mismo tipo q
                    // qName)
                    for (Nodo<Tag> nodo : lista) {
                            int count = 0;
                            // Recorro los hijos del elemento nodo de la lista
                            for (Nodo<Tag> hijo : nodo.getChildren()) {
                                    // Pregunto si existe algun elemento hijo que
                                    // coninicda con element
                                    if (hijo.getTag().getQname().equals(
                                                    elementoDtd.getQElement())) {
                                        count++;
                                    }
                            }
                            // Si no ahy ninguno tiro un error
                            if (count < 1) {
							throw new SAXException(SAXException.Tipo.ERROR, "El padre "+ qName+ "no contiene porlomenos un hijo de "+ elementoDtd.getQElement());
                            }

                    }

                    break;
                case '*':

                    // Recorro cada uno de los objetos de la lista
                    // (supustamente son todos los objetos del mismo tipo q
                    // qName)
                    for (Nodo<Tag> nodo : lista) {
                            int count = 0;
                            // Recorro los hijos del elemento nodo de la lista
                            for (Nodo<Tag> hijo : nodo.getChildren()) {
                                // Pregunto si existe algun elemento hijo que
                                // coninicda con element
                                if (hijo.getTag().getQname().equals(
                                                elementoDtd.getQElement())) {
                                    count++;
                                }
                            }
                            // Si no ahy ninguno tiro un error
                            if (count < 0) {
							throw new SAXException(SAXException.Tipo.ERROR,
                                                "Error de estructura");
                            }

                    }

                    break;
                case '?':
                    // Recorro cada uno de los objetos de la lista
                    // (supustamente son todos los objetos del mismo tipo q
                    // qName)
                    for (Nodo<Tag> nodo : lista) {
                            int count = 0;
                            // Recorro los hijos del elemento nodo de la lista
                            for (Nodo<Tag> hijo : nodo.getChildren()) {
                                // Pregunto si existe algun elemento hijo que
                                // coninicda con element
                                if (hijo.getTag().getQname().equals(
                                                elementoDtd.getQElement())) {
                                    count++;
                                }
                            }

                            if (count < 0 || count > 1) {
							throw new SAXException(SAXException.Tipo.ERROR,
                                        "Error de estructura: El padre " + qName
                                                        + " tiene mas de un hijo "
                                                        + elementoDtd.getQElement());
                            }

                    }

                    break;
                case '1':

                    // Recorro cada uno de los objetos de la lista
                    // (supustamente son todos los objetos del mismo tipo q
                    // qName)
                    for (Nodo<Tag> nodo : lista) {

                        int count = 0;
                        // Recorro los hijos del elemento nodo de la lista
                        for (Nodo<Tag> hijo : nodo.getChildren()) {
                            // Pregunto si existe algun elemento hijo que
                            // coninicda con element
                            if (hijo.getTag().getQname().equals(
                                            elementoDtd.getQElement())) {
                                count++;
                            }
                        }
                        if(!elementoDtd.getQElement().equals("PCDATA"))
                        {
                            // Valido que tenga solamente un hijo.
                            if (count != 1) {
								throw new SAXException(SAXException.Tipo.ERROR,
                                        "Error de estructura: El padre " + qName
                                                + " tiene mas o menos de un hijo "
                                                + elementoDtd.getQElement());
                            }
                        }
                    }

                    break;
                }

            }

        }

        if (tipo.equals("ATTLIST")) {
            if(isRequired)
            {
                //Recorro todos los nodos de la lista que son iguales al tipo qName
                for (Nodo<Tag> nodo : lista) {
                    //pregunto que exista el atributo
                    if(!nodo.getTag().getAtributos().get(0).getNombre().equals(qAtributo)){
						throw new SAXException(SAXException.Tipo.ERROR,
                                            "Error de estructura: El padre " + qName
                                                            + " no tiene el atributo requerido "
                                                            + qAtributo);
                    }

                }
            }
        }

    }

    public void endElement(String qName, Atributo attributes)
			throws SAXException {

    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
     * Aca arrancan los metodos del API ErrorHandler
     */


    /**
     * Este metodo recorre el arbol y busca todos los nodos que sean igual al
     * qName pasado por parametro retorna una lista de nodos<tag> que que sean
     * de tipo de qName : nombre del tag a buscar en el arbol nodo :
     * @param qName
     * @param nodo
     * @return nodos del tipo Tag
     */
    private ArrayList<Nodo<Tag>> getListadoDeNodosPorTag(String qName,
                    Nodo<Tag> nodo) {
        ArrayList<Nodo<Tag>> lista = new ArrayList<Nodo<Tag>>();
        // Esto lo pregunto solo por si ahi algun error con la funcion
        if (firts) {
            if (nodo.getTag().getQname().equals(qName)) {
                lista.add(nodo);
                firts = false;
            }
        }
        for (Nodo<Tag> hijo : nodo.getChildren()) {

            if (hijo.getTag().getQname().equals(qName)) {
                    lista.add(hijo);
            }
            if (hijo.getChildren().size() > 0) {
                if (hijo.getChildren() != null) {
                        lista.addAll(getListadoDeNodosPorTag(qName, hijo));
                }
            }
        }
        return lista;
    }
}
